package com.sfdk.graphics.engine.core
{
import com.sfdk.data.LayerBitmapKeyPair;
import com.sfdk.graphics.data.LayeredBitmap;
import com.sfdk.graphics.engine.api.IEditableMapObject;
import com.sfdk.graphics.engine.api.IGraphicRenderer;
import com.sfdk.graphics.engine.api.ILayerInformation;
import com.sfdk.graphics.engine.api.IMapObject;
import com.sfdk.graphics.engine.api.ITileset;
import com.sfdk.graphics.engine.api.IWalkableLayerInformation;
import com.sfdk.graphics.engine.core.enums.LayerType;
import com.sfdk.graphics.engine.data.TileInfo;
import com.sfdk.graphics.engine.utils.ExtendedBitmapData;
import com.sfdk.utils.IntPoint;

import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;

public class TileRenderer extends EventDispatcher implements IGraphicRenderer
	{
		private var _currentMap : IMapObject;

		private var solLayerContainer : ExtendedBitmapData;
		private var joueurLayerContainer : ExtendedBitmapData;
		private var upperLayerContainer : ExtendedBitmapData;
		private var walkableLayerContainer : ExtendedBitmapData;
		
		private var _mapWidth : int;
		private var _mapHeight : int;
		
		private var EMPTY_TILE : BitmapData;
		private var EMPTY_TILE_FILL : BitmapData;
		private var TILE_SIZE : int;
		private var BASE_RECT : Rectangle;
		
		public function loadMap(map : IMapObject) : void
		{
			_currentMap = map;
			_mapWidth = _currentMap.width;
			_mapHeight = _currentMap.height;
			TILE_SIZE = map.tileSize;
			BASE_RECT = new Rectangle(0, 0, TILE_SIZE, TILE_SIZE);

			EMPTY_TILE = new BitmapData(TILE_SIZE, TILE_SIZE, true, 0x00FFFFFF);
			EMPTY_TILE_FILL = new BitmapData(TILE_SIZE, TILE_SIZE, true, 0xFFFFFFFF);
			
			solLayerContainer = new ExtendedBitmapData(_mapWidth , _mapHeight , true , 0xFFFFFFFF, TILE_SIZE);
			joueurLayerContainer = new ExtendedBitmapData(_mapWidth , _mapHeight , true , 0, TILE_SIZE);
			upperLayerContainer = new ExtendedBitmapData(_mapWidth , _mapHeight , true , 0, TILE_SIZE);
			walkableLayerContainer = new ExtendedBitmapData(_mapWidth , _mapHeight , true , 0, TILE_SIZE);
			
			buildLayer();
		}
		
		public function forceRefresh(layer : ILayerInformation = null, usedTiles : Vector.<IntPoint> = null, eraseOnly : Boolean = false) : void
		{
			buildLayer(layer, usedTiles, eraseOnly);
		}
		
		public function get mapWidth() : int
		{
			return _mapWidth;
		}
		
		public function get mapHeight() : int
		{
			return _mapHeight;
		}
		
		
		private function buildLayer(layer : ILayerInformation = null, usedTiles : Vector.<IntPoint> = null, eraseOnly : Boolean = false) : void
		{
			
			var solLayers:Vector.<ILayerInformation>;
			var joueurLayers:Vector.<ILayerInformation>;
			var upperLayers:Vector.<ILayerInformation>;
			var solLayer : ILayerInformation;
			var joueurLayer : ILayerInformation;
			var upperLayer : ILayerInformation;
			var walkableLayer : IWalkableLayerInformation;
			var moduleList : Vector.<LayerBitmapKeyPair>;
			
			if(layer == null)
			{
				solLayerContainer.clear();
				joueurLayerContainer.clear();
				upperLayerContainer.clear();
				walkableLayerContainer.clear();
				
				solLayers = _currentMap.solLayers;
				
				for each(solLayer in solLayers)
				{
					if(solLayer.isVisible)
						fillLayer(solLayerContainer, solLayer, null, eraseOnly);
				}
				
				joueurLayers = _currentMap.joueurLayers;
				for each(joueurLayer in joueurLayers)
				{
					if(joueurLayer.isVisible)
						fillLayer(joueurLayerContainer, joueurLayer, null, eraseOnly);
				}
				
				upperLayers = _currentMap.upperLayers;
				
				for each(upperLayer in upperLayers)
				{
					if(upperLayer.isVisible)
						fillLayer(upperLayerContainer, upperLayer, null, eraseOnly);
				}
				
				walkableLayer = _currentMap.walkableLayer;
				if(walkableLayer.isVisible)
					fillLayer(walkableLayerContainer, walkableLayer, null, eraseOnly);
				
				if(_currentMap is IEditableMapObject)
				{
					moduleList = (_currentMap as IEditableMapObject).moduleList;
					fillModules(moduleList);
				}
				
				dispatchEvent(new Event("layerBuilt"));
			}
			else
			{
				if(layer.isVisible)
					fillLayer(solLayerContainer, solLayer, null, eraseOnly);
				switch(layer.layerType)
				{
					case LayerType.SOL_TYPE:
						if(usedTiles == null)
							solLayerContainer.clear(0xFFFFFFFF);
						else
							clearLayer(solLayerContainer, usedTiles, true);
						solLayers = _currentMap.solLayers;
						for each(solLayer in solLayers)
						{
							if(solLayer.isVisible)
								fillLayer(solLayerContainer, solLayer, usedTiles, eraseOnly);
						}
					break;
					case LayerType.JOUEUR_TYPE:
						if(usedTiles == null)
							joueurLayerContainer.clear();
						else
							clearLayer(joueurLayerContainer, usedTiles, true);
						joueurLayers = _currentMap.joueurLayers;
						for each(joueurLayer in joueurLayers)
						{
							if(joueurLayer.isVisible)
								fillLayer(joueurLayerContainer, joueurLayer, usedTiles, eraseOnly);
						}
					break;
					case LayerType.UPPER_TYPE:
						if(usedTiles == null)
							upperLayerContainer.clear();
						else
							clearLayer(upperLayerContainer, usedTiles);
						upperLayers = _currentMap.upperLayers;
						for each(upperLayer in upperLayers)
						{
							if(upperLayer.isVisible)
								fillLayer(upperLayerContainer, upperLayer, usedTiles, eraseOnly && upperLayer == layer);
						}
					break;
					case LayerType.WALKABLE_TYPE:
						if(usedTiles == null)
							walkableLayerContainer.clear();
						else
							clearLayer(walkableLayerContainer, usedTiles);
						walkableLayer = _currentMap.walkableLayer;
						if(walkableLayer.isVisible)
							fillLayer(walkableLayerContainer, walkableLayer, usedTiles, eraseOnly);
					break;
				}
				
				if(_currentMap is IEditableMapObject)
				{
					moduleList = (_currentMap as IEditableMapObject).moduleList;
					fillModules(moduleList);
				}
				
				dispatchEvent(new Event("layerBuilt"));
			}
		}
		
		private function fillLayer(layer : ExtendedBitmapData , layerData : ILayerInformation, usedTiles : Vector.<IntPoint> = null, eraseOnly : Boolean = false) : void
		{
			if(layerData != null)
			{
				var info : TileInfo;
				var destPoint : Point = new Point();
				var copyRect : Rectangle = BASE_RECT;
				var currentTileset : ITileset;
				var j : int , i : int;
				var tileHeight : int = _currentMap.heightInTile;
				var tileWidth : int = _currentMap.widthInTile;
				
				if(usedTiles == null)
				{
					for(j = 0; j < tileHeight; j++)
					{
						for(i = 0; i < tileWidth; i++)
						{
							info = layerData.getInfo(i , j);
							destPoint.x = i * TILE_SIZE;
							destPoint.y = j * TILE_SIZE;
							
							if(info != null && info.tileset != null)
							{
								currentTileset = info.tileset;
								layer.copyPixels(currentTileset.getTile(info.tileX, info.tileY) , copyRect , destPoint , null , null , true);
							}
							else
								layer.copyPixels(EMPTY_TILE , copyRect , destPoint , null , null , true);
						}
					}
				}
				else
				{
					var currPoint : IntPoint;
					var usedTileLength : int = usedTiles.length;
					for(i = 0; i < usedTileLength; i++)
					{
						currPoint = usedTiles[i];
						info = layerData.getInfo(currPoint.x , currPoint.y);
						
						destPoint.x = currPoint.x * TILE_SIZE;
						destPoint.y = currPoint.y * TILE_SIZE;
						
						if(!eraseOnly && info != null && info.tileset != null)
						{
							currentTileset = info.tileset;
							layer.copyPixels(currentTileset.getTile(info.tileX, info.tileY) , copyRect , destPoint , null , null , true);
						}
						else
							layer.copyPixels(EMPTY_TILE , copyRect , destPoint , null , null , true);
					}
				}
			}
		}
		
		private function fillModules(moduleList : Vector.<LayerBitmapKeyPair>) : void
		{
			var moduleElement : LayerBitmapKeyPair;
			var layerBitmap : LayeredBitmap;
			var copyRectangle : Rectangle = new Rectangle();
			var copyPoint : Point;
			for each(moduleElement in moduleList)
			{
				layerBitmap = moduleElement.layerBitmap;
				copyPoint = moduleElement.position.clone();
				copyPoint.x = copyPoint.x * TILE_SIZE;
				copyPoint.y = copyPoint.y * TILE_SIZE;
				copyRectangle.width = layerBitmap.layeredData[0].width; 
				copyRectangle.height = layerBitmap.layeredData[0].height;
				
				solLayerContainer.copyPixels(layerBitmap.layeredData[0], copyRectangle, copyPoint, null, copyPoint, true);
				solLayerContainer.copyPixels(layerBitmap.layeredData[1], copyRectangle, copyPoint, null, copyPoint, true);
				solLayerContainer.copyPixels(layerBitmap.layeredData[2], copyRectangle, copyPoint, null, copyPoint, true);
//				
//				joueurLayerContainer.copyPixels(layerBitmap.layeredData[3], copyRectangle, copyPoint, null, copyPoint, true);
//				
//				upperLayerContainer.copyPixels(layerBitmap.layeredData[4], copyRectangle, copyPoint, null, copyPoint, true);
//				upperLayerContainer.copyPixels(layerBitmap.layeredData[5], copyRectangle, copyPoint, null, copyPoint, true);
//				upperLayerContainer.copyPixels(layerBitmap.layeredData[6], copyRectangle, copyPoint, null, copyPoint, true);
			}
		}
		
		private function clearLayer(layer : ExtendedBitmapData, usedTiles : Vector.<IntPoint> = null , isSol : Boolean = false) : void
		{
			var info : TileInfo;
			var destPoint : Point = new Point();
			var copyRect : Rectangle = BASE_RECT;
			var j : int , i : int;
			var currPoint : IntPoint;
			var usedTileLength : int = usedTiles.length;
			for(i = 0; i < usedTileLength; i++)
			{
				currPoint = usedTiles[i];
				destPoint.x = currPoint.x * TILE_SIZE;
				destPoint.y = currPoint.y * TILE_SIZE;
				if(isSol)
					layer.copyPixels(EMPTY_TILE_FILL , copyRect , destPoint , null , null , false);
				else	
					layer.copyPixels(EMPTY_TILE, copyRect , destPoint , null , null , false);
			}
		}
		public function copySolBitmap(destBitmap : BitmapData , rect : Rectangle , point : Point) : void
		{
			solLayerContainer.copyPixelsTo(destBitmap, rect, point, null, null, true)
		}
		
		public function copyJoueurBitmap(destBitmap : BitmapData , rect : Rectangle , point : Point) : void
		{
			joueurLayerContainer.copyPixelsTo(destBitmap, rect , point , null , null , true);
		}
		
		public function copyDessusBitmap(destBitmap : BitmapData , rect : Rectangle , point : Point) : void
		{
			upperLayerContainer.copyPixelsTo(destBitmap, rect , point , null , null , true);
		}
		
		public function copyWalkableBitmap(destBitmap : BitmapData , rect : Rectangle , point : Point) : void
		{
			walkableLayerContainer.copyPixelsTo(destBitmap, rect , point , null , null , true);
		}
		
		public function getThumbnail() : BitmapData
		{
			var thumbnailData : BitmapData = solLayerContainer.getThumbnail(true);
			var sourceRect : Rectangle = new Rectangle(0, 0, thumbnailData.width, thumbnailData.height);
			var destPoint : Point = new Point();

			thumbnailData.copyPixels(joueurLayerContainer.getThumbnail(), sourceRect, destPoint, null, null, true);
			thumbnailData.copyPixels(upperLayerContainer.getThumbnail(), sourceRect, destPoint, null, null, true);

			return thumbnailData;
		}
	}
}